lib: implement CheckoutOptions::filter (hackishly)
authorFelix Krull <f_krull@gmx.de>
Fri, 31 May 2019 19:56:44 +0000 (21:56 +0200)
committerColin Walters <walters@verbum.org>
Fri, 6 May 2022 16:53:54 +0000 (12:53 -0400)
rust-bindings/rust/conf/ostree.toml
rust-bindings/rust/src/auto/enums.rs
rust-bindings/rust/src/auto/mod.rs
rust-bindings/rust/src/lib.rs
rust-bindings/rust/src/repo_checkout_at_options.rs
rust-bindings/rust/tests/repo.rs

index 235cfbe1c75084386653f39d351e662f0a5e6747..4eec250fa2537eaeea032926b8d1d3d6a2a5d265 100644 (file)
@@ -24,6 +24,7 @@ generate = [
     "OSTree.MutableTree",
     "OSTree.ObjectType",
     "OSTree.Remote",
+    "OSTree.RepoCheckoutFilterResult",
     "OSTree.RepoCheckoutMode",
     "OSTree.RepoCheckoutOverwriteMode",
     "OSTree.RepoCommitModifier",
index 148253260dbccb477203e9cd082d619786452956..466b8bba728e9249553bb4158cb81fd704f27965 100644 (file)
@@ -242,6 +242,53 @@ impl FromGlib<ostree_sys::OstreeObjectType> for ObjectType {
     }
 }
 
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Clone, Copy)]
+pub enum RepoCheckoutFilterResult {
+    Allow,
+    Skip,
+    #[doc(hidden)]
+    __Unknown(i32),
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+impl fmt::Display for RepoCheckoutFilterResult {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "RepoCheckoutFilterResult::{}", match *self {
+            RepoCheckoutFilterResult::Allow => "Allow",
+            RepoCheckoutFilterResult::Skip => "Skip",
+            _ => "Unknown",
+        })
+    }
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[doc(hidden)]
+impl ToGlib for RepoCheckoutFilterResult {
+    type GlibType = ostree_sys::OstreeRepoCheckoutFilterResult;
+
+    fn to_glib(&self) -> ostree_sys::OstreeRepoCheckoutFilterResult {
+        match *self {
+            RepoCheckoutFilterResult::Allow => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_ALLOW,
+            RepoCheckoutFilterResult::Skip => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_SKIP,
+            RepoCheckoutFilterResult::__Unknown(value) => value
+        }
+    }
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[doc(hidden)]
+impl FromGlib<ostree_sys::OstreeRepoCheckoutFilterResult> for RepoCheckoutFilterResult {
+    fn from_glib(value: ostree_sys::OstreeRepoCheckoutFilterResult) -> Self {
+        match value {
+            0 => RepoCheckoutFilterResult::Allow,
+            1 => RepoCheckoutFilterResult::Skip,
+            value => RepoCheckoutFilterResult::__Unknown(value),
+        }
+    }
+}
+
 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
 #[derive(Clone, Copy)]
 pub enum RepoCheckoutMode {
index 887d32599f41f4dd022db02e19f36e3ede8fed03..17a94c61e4f02a709073945558abf29dd04653be 100644 (file)
@@ -71,6 +71,8 @@ pub use self::enums::DeploymentUnlockedState;
 pub use self::enums::GpgSignatureAttr;
 pub use self::enums::GpgSignatureFormatFlags;
 pub use self::enums::ObjectType;
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+pub use self::enums::RepoCheckoutFilterResult;
 pub use self::enums::RepoCheckoutMode;
 pub use self::enums::RepoCheckoutOverwriteMode;
 pub use self::enums::RepoMode;
index ea3130f63cdea0bf8930101308a54ea86fa8e3b6..904ac98f7d774b3e6a402a2e0fb1c8c33be752f5 100644 (file)
@@ -34,11 +34,13 @@ pub use crate::auto::*;
 mod collection_ref;
 mod object_name;
 mod repo;
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
 mod repo_checkout_at_options;
 #[cfg(any(feature = "v2018_6", feature = "dox"))]
 pub use crate::collection_ref::*;
 pub use crate::object_name::*;
 pub use crate::repo::*;
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
 pub use crate::repo_checkout_at_options::*;
 
 // tests
index ed65b17a62f57f249c28c93977063a46159f4e46..b69e2c30676170fab0034412526eb0afc279783e 100644 (file)
@@ -1,11 +1,15 @@
-use glib::translate::{Stash, ToGlib, ToGlibPtr};
+use glib::translate::{FromGlibPtrNone, Stash, ToGlib, ToGlibPtr};
+use glib_sys::gpointer;
 use libc::c_char;
-use ostree_sys::OstreeRepoCheckoutAtOptions;
-use std::path::PathBuf;
+use ostree_sys::{OstreeRepo, OstreeRepoCheckoutAtOptions, OstreeRepoCheckoutFilterResult};
+use std::path::{Path, PathBuf};
+use {Repo, RepoCheckoutFilterResult};
 use {RepoCheckoutMode, RepoCheckoutOverwriteMode};
 use {RepoDevInoCache, SePolicy};
 
-#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+pub type RepoCheckoutFilter =
+    Option<Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>>;
+
 pub struct RepoCheckoutAtOptions {
     pub mode: RepoCheckoutMode,
     pub overwrite_mode: RepoCheckoutOverwriteMode,
@@ -18,9 +22,8 @@ pub struct RepoCheckoutAtOptions {
     pub force_copy_zerosized: bool,
     pub subpath: Option<PathBuf>,
     pub devino_to_csum_cache: Option<RepoDevInoCache>,
-    // TODO: those thingamajigs
-    // pub filter: OstreeRepoCheckoutFilter,
-    // pub filter_user_data: gpointer,
+    // TODO: might be interesting to turn this into a type parameter
+    pub filter: RepoCheckoutFilter,
     pub sepolicy: Option<SePolicy>,
     pub sepolicy_prefix: Option<String>,
 }
@@ -39,6 +42,7 @@ impl Default for RepoCheckoutAtOptions {
             force_copy_zerosized: false,
             subpath: None,
             devino_to_csum_cache: None,
+            filter: None,
             sepolicy: None,
             sepolicy_prefix: None,
         }
@@ -47,6 +51,21 @@ impl Default for RepoCheckoutAtOptions {
 
 type StringStash<'a, T> = Stash<'a, *const c_char, Option<T>>;
 
+unsafe extern "C" fn filter_trampoline(
+    repo: *mut OstreeRepo,
+    path: *const c_char,
+    stat: *mut libc::stat,
+    user_data: gpointer,
+) -> OstreeRepoCheckoutFilterResult {
+    // TODO: handle unwinding
+    let closure =
+        user_data as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>;
+    let repo = FromGlibPtrNone::from_glib_none(repo);
+    let path: PathBuf = FromGlibPtrNone::from_glib_none(path);
+    let result = (*closure)(&repo, &path, &*stat);
+    result.to_glib()
+}
+
 impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions {
     type Storage = (
         Box<OstreeRepoCheckoutAtOptions>,
@@ -75,6 +94,21 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
         let sepolicy = self.sepolicy.to_glib_none();
         options.sepolicy = sepolicy.0;
 
+        if let Some(filter) = &self.filter {
+            options.filter_user_data = filter
+                as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
+                as gpointer;
+            options.filter = Some(
+                filter_trampoline
+                    as unsafe extern "C" fn(
+                        *mut OstreeRepo,
+                        *const c_char,
+                        *mut libc::stat,
+                        gpointer,
+                    ) -> OstreeRepoCheckoutFilterResult,
+            );
+        }
+
         Stash(options.as_ref(), (options, subpath, sepolicy_prefix))
     }
 }
@@ -83,7 +117,7 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
 mod tests {
     use super::*;
     use gio::{File, NONE_CANCELLABLE};
-    use glib_sys::{GFALSE, GTRUE};
+    use glib_sys::{gpointer, GFALSE, GTRUE};
     use ostree_sys::{
         OSTREE_REPO_CHECKOUT_MODE_NONE, OSTREE_REPO_CHECKOUT_MODE_USER,
         OSTREE_REPO_CHECKOUT_OVERWRITE_NONE, OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL,
@@ -132,6 +166,9 @@ mod tests {
             force_copy_zerosized: true,
             subpath: Some("sub/path".into()),
             devino_to_csum_cache: Some(RepoDevInoCache::new()),
+            filter: Some(Box::new(|_repo, _path, _stat| {
+                RepoCheckoutFilterResult::Skip
+            })),
             sepolicy: Some(SePolicy::new(&File::new_for_path("a/b"), NONE_CANCELLABLE).unwrap()),
             sepolicy_prefix: Some("prefix".into()),
         };
@@ -161,8 +198,25 @@ mod tests {
             );
             assert_eq!((*ptr).unused_ints, [0; 6]);
             assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
-            assert_eq!((*ptr).filter, None);
-            assert_eq!((*ptr).filter_user_data, ptr::null_mut());
+            assert_eq!(
+                (*ptr).filter,
+                Some(
+                    filter_trampoline
+                        as unsafe extern "C" fn(
+                            *mut OstreeRepo,
+                            *const c_char,
+                            *mut libc::stat,
+                            gpointer,
+                        )
+                            -> OstreeRepoCheckoutFilterResult
+                )
+            );
+            assert_eq!(
+                (*ptr).filter_user_data,
+                options.filter.as_ref().unwrap()
+                    as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
+                    as gpointer
+            );
             assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0);
             assert_eq!(
                 CStr::from_ptr((*ptr).sepolicy_prefix),
index 1ae4361b700bf75a853dbb042a4f531e59c1eb91..12038ded36204cb7b9f259c661afbd7640cfec54 100644 (file)
@@ -13,8 +13,8 @@ use gio::prelude::*;
 use gio::NONE_CANCELLABLE;
 use glib::prelude::*;
 use ostree::{
-    ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutMode, RepoCheckoutOverwriteMode,
-    RepoDevInoCache,
+    ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutFilterResult, RepoCheckoutMode,
+    RepoCheckoutOverwriteMode, RepoDevInoCache,
 };
 use std::os::unix::io::AsRawFd;
 
@@ -161,6 +161,9 @@ fn should_checkout_at_with_options() {
                 force_copy: true,
                 force_copy_zerosized: true,
                 devino_to_csum_cache: Some(RepoDevInoCache::new()),
+                filter: Some(Box::new(|_repo, _path, _stat| {
+                    RepoCheckoutFilterResult::Allow
+                })),
                 ..Default::default()
             }),
             dirfd.as_raw_fd(),
@@ -172,3 +175,36 @@ fn should_checkout_at_with_options() {
 
     assert_test_file(checkout_dir.path());
 }
+
+#[test]
+#[cfg(feature = "v2016_8")]
+fn should_checkout_at_with_filter() {
+    let test_repo = TestRepo::new();
+    let checksum = test_repo.test_commit("test");
+    let checkout_dir = tempfile::tempdir().expect("checkout dir");
+
+    let dirfd = openat::Dir::open(checkout_dir.path()).expect("openat");
+    test_repo
+        .repo
+        .checkout_at(
+            Some(&RepoCheckoutAtOptions {
+                filter: Some(Box::new(|_repo, path, _stat| {
+                    if let Some("testfile") = path.file_name().map(|s| s.to_str().unwrap()) {
+                        RepoCheckoutFilterResult::Skip
+                    } else {
+                        RepoCheckoutFilterResult::Allow
+                    }
+                })),
+                ..Default::default()
+            }),
+            dirfd.as_raw_fd(),
+            "test-checkout",
+            &checksum,
+            NONE_CANCELLABLE,
+        )
+        .expect("checkout at");
+
+    let testdir = checkout_dir.path().join("test-checkout").join("testdir");
+    assert!(std::fs::read_dir(&testdir).is_ok());
+    assert!(std::fs::File::open(&testdir.join("testfile")).is_err());
+}